package  nid
{
	
	
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.events.NetStatusEvent;
	import flash.media.Camera;
	import flash.media.Microphone;
	import flash.media.Video;
	import flash.net.GroupSpecifier;
	import flash.net.NetConnection;
	import flash.net.NetGroupReceiveMode;
	import flash.net.NetGroupReplicationStrategy;
	import flash.net.NetGroupSendMode;
	import flash.system.Capabilities;
	import flash.utils.ByteArray;
	import flash.net.NetGroup;
	import flash.net.NetStream;
	import nid.utils.Strings;
	
	import nid.display.VideoDisplay;
	import nid.net.NetStreamClient;
	import nid.net.NetStreamDataEvent;
	
	/**
	 * ...
	 * @author Nidin.P.Vinayak
	 */
	public class Air_LAN_Messenger extends Sprite
	{
		private var SERVER_LESS:Boolean = true;
		private const SERVER:String = "rtmfp://stratus.adobe.com/";
		private const DEVKEY:String = "d686a308d66dfab49e517141-7fde4acf4f89";			
		
		private var _netConnection:NetConnection;
		
		private var _groupSpecifier:GroupSpecifier;
		
		private var _outgoingStream:NetStream;
		private var _incomingStream:NetStream;
		
		private var _streamClient:NetStreamClient;			
		private var _netGroup:NetGroup;
		
		private var _nearID:String;
		private var _groupSpec:String;
		private var _estimatedP2PMembers:Number;
		
		private var _netConnectionConnected:Boolean;
		private var _outgoingStreamOn:Boolean;
		private var _incomingStreamOn:Boolean;
		private var _netGroupConnected:Boolean;
		public var videoDisplay1:VideoDisplay;
		public var videoDisplay2:VideoDisplay;
		
		public function Air_LAN_Messenger() 
		{
			
			configUI();
		}
		
		private function configUI():void {
			
			submit.addEventListener(MouseEvent.CLICK, onPostMessage);
			toggleNetConnection.addEventListener(MouseEvent.CLICK, _initNetConnection);
			toggleNetGroup.addEventListener(MouseEvent.CLICK, onJoinNetGroup);
			startOutgoingStream.addEventListener(MouseEvent.CLICK, onStartOutgoingStream);
			startIncomingStream.addEventListener(MouseEvent.CLICK, onStartIncomingStream);
			
			//onCreationComplete();
			checkStatus();
		}
		private function checkStatus():void {
			_status.text = _netGroupConnected? 'Estimated Nodes in the P2P Group: ' + _estimatedP2PMembers : 'Not connected to P2P Group';
		}
		
		protected function onCreationComplete():void
		{
			_logMsg( "Flash Player Version: " + Capabilities.version );
			_logMsg( "Is Flash Player a Debugger? " + Capabilities.isDebugger );
			
			_initNetConnection();	
		}

		protected function onNSDataReceived( event:NetStreamDataEvent ):void
		{
			_logMsg( String( event.data ) );
		}
		
		protected function onNetStatus( event:NetStatusEvent ):void
		{
			_logMsg( event.info.code );
			checkStatus();
			trace(event.info.code);
			switch( event.info.code )
			{
				case "NetConnection.Connect.Success":
				{
					_netConnectionConnected = true;
					_nearID = event.target.nearID;
					_logMsg( "Connected - near ID: " + _nearID );
					_createGroupSpec();
					
					break;
				}
				case "NetConnection.Connect.Closed":
				case "NetConnection.Connect.Failed":
				case "NetConnection.Connect.Rejected":
				case "NetConnection.Connect.AppShutdown":
				case "NetConnection.Connect.InvalidApp":
				{
					
					if( _netGroup )
					{
						clearNetGroup();
					}
					
					_outgoingStream = null;
					_incomingStream = null;		
					
					_netConnectionConnected = false;
					_outgoingStreamOn = false;
					_incomingStreamOn = false;
					
					break;
				}
				case "NetStream.Connect.Success":
				{
					if( _outgoingStreamOn )
					{
						_attachLocalVideoAndAudio();
					}
					else
					{	
						_attachPeerVideoAndAudio();
					}	
					break;		
				}
				case "NetStream.Connect.Rejected":
				case "NetStream.Connect.Failed":
				{
					if( _outgoingStreamOn )
					{
						_outgoingStreamOn = false;						
					}
					else
					{
						_incomingStreamOn = false;
					}
					break;
				}	
				case "NetStream.Publish.Start":
				{
					// outgoing stream is now published.
					break;
				}
				case "NetStream.MulticastStream.Reset":
				case "NetStream.Buffer.Full":
				default:
				{
					break;	
				}
				case "NetGroup.Connect.Success":
				{
					_netGroupConnected = true;
					_logMsg( "Connected - Group ID: " + _groupSpec );
					_estimatedP2PMembers = _netGroup.estimatedMemberCount;						
					break;
				}		
				case "NetGroup.Connect.Rejected":
				case "NetGroup.Connect.Failed":
				{
					clearNetGroup();
					break;
				}
			}
		}	

		// handles NetGroup NetStatus events
		protected function onNetGroupStatus( event:NetStatusEvent ):void
		{
			_logMsg( event.info.code );				
			trace(Strings.toStrings(event.info));
			checkStatus();
			switch(event.info.code)
			{								
				
				case "NetGroup.LocalCoverage.Notify":
					
					for each(var st:String in event.info) {
						trace(st);
					}
					
				break;
				
				case "NetGroup.Posting.Notify":
				{
					chat_ta.text = event.info.message.user + ": " + String( event.info.message.message )
						+ "\n" + chat_ta.text; 
					break;
				}																		
				case "NetGroup.Neighbor.Connect":
				{
					trace(_netGroup.sendToNeighbor("thish is a test", NetGroupSendMode.NEXT_INCREASING));
					if( event.info.neighbor != _netGroup.convertPeerIDToGroupAddress( _nearID ) )
					{
						trace( 'Neighbor ' + event.info.neighbor + ' has connected' );
						_estimatedP2PMembers = _netGroup.estimatedMemberCount;
					}	
					break;
				}
				case "NetGroup.Neighbor.Disconnect":
				{					
					trace( 'Neighbor ' + event.info.neighbor + ' has disconnected' );	
					_estimatedP2PMembers = _netGroup.estimatedMemberCount;						
					break;
				}
				case "NetGroup.SendTo.Notify": // event.info.message, event.info.from, event.info.fromLocal
				case "NetGroup.MulticastStream.PublishNotify": // event.info.name
				case "NetGroup.MulticastStream.UnpublishNotify": // event.info.name
				case "NetGroup.Replication.Fetch.SendNotify": // event.info.index
				case "NetGroup.Replication.Fetch.Failed": // event.info.index
				case "NetGroup.Replication.Fetch.Result": // event.info.index, event.info.object
				case "NetGroup.Replication.Request": // event.info.index, event.info.requestID	
				default:
				{
					break;
				}
			}									
		}

		protected function onStartOutgoingStream(event:MouseEvent):void
		{			
			_outgoingStream = new NetStream( _netConnection, _groupSpec );
			
			_outgoingStream.bufferTime = 0;
			_outgoingStream.backBufferTime = 0;
			
			_outgoingStream.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus );
			
			_outgoingStream.client = _streamClient;
			_outgoingStreamOn = true;
			
			checkStatus();
		}

		// For inbound stream
		protected function onStartIncomingStream(event:MouseEvent):void
		{					
			_incomingStream = new NetStream( _netConnection, _groupSpec );
			
			_incomingStream.bufferTime = 0;
			_incomingStream.backBufferTime = 0;

			_incomingStream.client = _streamClient;
			
			_incomingStreamOn = true;
			
			checkStatus();
		}
		
		private function _createGroupSpec():void
		{
			trace('_createGroupSpec');
			_groupSpecifier = new GroupSpecifier( _groupName.text );
			
			_groupSpecifier.multicastEnabled = true;
			_groupSpecifier.objectReplicationEnabled = true;
			_groupSpecifier.postingEnabled = true;
			_groupSpecifier.routingEnabled = true;
			//_groupSpecifier.serverChannelEnabled = true;
			_groupSpecifier.ipMulticastMemberUpdatesEnabled = true;
			_groupSpecifier.addIPMulticastAddress("225.225.0.1:30303");
			
			_groupSpec = _groupSpecifier.groupspecWithoutAuthorizations();
		}
		
		protected function onJoinNetGroup(event:MouseEvent):void
		{
			_netGroup = new NetGroup( _netConnection, _groupSpec );
			_netGroup.addEventListener( NetStatusEvent.NET_STATUS, onNetGroupStatus );	
			
			checkStatus();
		}
		
		protected function clearNetGroup():void
		{
			_netGroup.close();
			_netGroupConnected = false;
			_netGroup.removeEventListener( NetStatusEvent.NET_STATUS, onNetGroupStatus );
			_netGroup = null;
		}

		protected function onPostMessage(event:MouseEvent):void
		{
			trace('onPostMessage\n' + message_ti.text, _netGroup);
			checkStatus();
			if( _netGroup && !message_ti.text )
			{
				_netGroup.post( new ByteArray() );
			}
			else
			{
				_netGroup.post( { user:user_name.text,message:message_ti.text, id:Math.random() } );
				chat_ta.text = user_name.text+": "+message_ti.text
					+ "\n" + chat_ta.text; 
			}
		}

		private function _initNetConnection(e:MouseEvent=null):void
		{
			_netConnection = new NetConnection();
			_streamClient = new NetStreamClient();
			
			_streamClient.addEventListener( NetStreamDataEvent.DATA_RECEIVED, onNSDataReceived )
			_netConnection.addEventListener (NetStatusEvent.NET_STATUS, onNetStatus );
			
			if (SERVER_LESS) {
				_netConnection.connect("rtmfp:");
			}else{
				_netConnection.connect( SERVER + DEVKEY );
			}
		}
		
		private function _attachLocalVideoAndAudio():void
		{				
			trace('_attachLocalVideoAndAudio');
			var camera:Camera = Camera.getCamera();
			var mic:Microphone = Microphone.getMicrophone();				

			if(mic)
			{
				_outgoingStream.attachAudio(mic);
			}
			
			if(camera)
			{
				videoDisplay1.attachCamera(camera);
				_outgoingStream.attachCamera(camera);
			}
			_outgoingStream.publish( "P2PVideo" );			
		}
		
		private function _attachPeerVideoAndAudio():void
		{
			var remoteVideo:Video = new Video();
			remoteVideo.attachNetStream( _incomingStream );
			videoDisplay2.addVideo( remoteVideo );
			_incomingStream.play("P2PVideo");			
		}		
		
		
		
		private function _logMsg( p_msg:String):void
		{
			console_ta.text = "=================\n" 
				+ p_msg + "\n"
				+ console_ta.text;
		}
	}

}
